为一块新的LCD屏编写了驱动,然后编译进内核,下载到开发板上,这时候,屏幕也许除了背光,没有其它任何的显示,这个时候,我们应该怎么进行调试呢?下面是几个最基本的问题:

下面是一些我现在用到的办法:

命令行查看FrameBuffer的信息

# cat /proc/fb		//在/proc目录,查看当前系统中的帧缓冲设备
0 jz-lcd
1 jz-slcd
# ls /dev/fb*		//查看/dev目录下的不同的帧缓冲设备
/dev/fb0  /dev/fb1

可以看到,现在系统中有两个帧缓冲设备。其中第0个帧缓存设备/dev/fb0,对应的是LCD控制器(jz-lcd)的驱动,而第1个帧缓存设备/dev/fb1,就是智能LCD控制器(jz-slcd)的驱动。可以在./drivers/video/目录下面的驱动文件(这里是jzlcd.c文件和jz4740_slcd.c文件)中看到/proc/fb中命名和注册。

static struct lcd_cfb_info * jzfb_alloc_fb_info(void)
{
    ……
    strcpy(cfb->fb.fix.id, "jz-lcd");	//这里的字符串"jz-lcd"就是在/proc/fb中看到的打印信息,修改这里可以在/proc/fb中看到相应的改动
    ……
}


static int __init jzfb_init(void)
{
    ……
    err = register_framebuffer(&cfb->fb);
    if (err < 0) {
	    dprintk("jzfb_init(): register framebuffer err.\n");
	    goto failed;
    }
    ……
}

register_framebuffer()函数就是将LCD控制器(jz-lcd)的驱动注册到frame buffer中,这样才有我们看到的/proc/fb中的帧缓存设备0 jz-lcd以及/dev/fb0设备描述符。同理,我们还可以同时将智能LCD控制器(jz-slcd)的驱动注册到frame buffer中。

可以看到,frame buffer作为驱动程序,是各种不同的显示设备对OS,应用层的一个统一的接口,一个驱动接口。

在刚开始的时候,由于没有在.config文件中将jz-lcd驱动选项去掉,所以加载了两个帧缓冲设备,如上面所示。

命令行查看FrameBuffer的中断信息

查看到帧设备之后,那么帧设备有没有工作呢?我们可以通过产看中断的办法,大概的看到帧设备是否工作。

# cat /proc/interrupts
           CPU0
  3:          2            INTC  ohci_hcd:usb1
  9:       5758            INTC  serial
 14:          0            INTC  MMC/SD
 15:          0            INTC  rtc
 17:          0            INTC  cim
 23:      73137            INTC  jz-timerirq
 30:          0            INTC  lcd
 32:          0             DMA  auto
 33:          0             DMA  MMC Rx
 34:          0             DMA  MMC Tx
 35:          0             DMA  audio adc
 36:          0             DMA  audio dac
150:          0            GPIO  udc_pnp
158:          0            GPIO  MMC card detect
173:          0            GPIO  poweroff

ERR:          0

通过打印/proc/interrupts的信息,可以看到30号中断,对应上面的jz-lcd驱动,而32号中断,是DMA中断,名字为auto,这其实对应jz-slcd的中断,jz4740_slcd.c文件中有下面的代码为证:

static int slcd_dma_init(void)
{
	/* Request DMA channel and setup irq handler */
	dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", slcd_dma_irq, 0, NULL);
	if (dma_chan < 0) {
		printk("Request DMA Failed\n");
		return -1;
	}
	printk("DMA channel %d is requested by SLCD!\n", dma_chan);
	……
}

可以看到,这驱动中,通过jz_request_dma()函数请求可一个DMA通道,名字为auto,并且注册了DMA中断函数slcd_dma_irq()。通过查看内核启动信息,也可以验证这一点。

# cat /proc/
……
DMA channel 0 is requested by SLCD!
……

其中,cat /proc/interrupts的输出第二列表示进入这个中断的次数。可以看到,进入jz-lcd和auto的中断次数为0次,也就是从没有进入过中断。没有进入过中断,就没有DMA将图像从内存写入显存(GRAM)的过程,LCD上没有图像显示,也就不足为奇了。

查看内核启动信息

查看内核启动时的输出信息是最直接,有效的调试方式。可以在命令行下使用klog命令,也可以直接接串口获得内核启动信息。下面就下面的输出做出分析:


fb_alloc_cmap,fb.cmap.len:256....
dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:0,RGBt:(0,0,0,65535)
dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:1,RGBt:(0,0,43690,65535)
……		//省略了regno:2~regno:253
dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:254,RGBt:(44767,65503,65519,65535)
dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:255,RGBt:(65535,65535,65535,65535)
jzfb_set_var: after fb_set_cmap...
slcd_hw_init---------jzfb.w:240jzfb.h:320,jzfb.bpp:16
in order to init slcd SLCD_CFG=0x4400
SLCDC: PixClock:16000000 LcdClock:28000000
SLCD_CFG=0x4400
jzfb_set_pardbg::drivers/video/jz4740_slcd.c,LINE(460): var.yoffset: 0
dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:0,RGBt:(0,0,0,65535)
dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:1,RGBt:(0,0,43690,65535)
……		//省略了regno:2~regno:13
dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:14,RGBt:(65535,65535,21845,65535)
dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:15,RGBt:(65535,65535,65535,65535)
Console: switching to colour frame buffer device 30x40
dbg::drivers/video/jz4740_slcd.c,LINE(460): var.yoffset: 0
dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:0,RGBt:(0,0,0,65535)
dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:1,RGBt:(0,0,43690,65535)
……		//省略了regno:2~regno:13
dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:14,RGBt:(65535,65535,21845,65535)
dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:15,RGBt:(65535,65535,65535,65535)
dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:0,RGBt:(0,0,0,65535)
dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:1,RGBt:(0,0,43690,65535)
……		//省略了regno:2~regno:13
dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:14,RGBt:(65535,65535,21845,65535)
dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:15,RGBt:(65535,65535,65535,65535)
fb0: jz-ny-slcd frame buffer device, using 256K of video memory
DMA channel 0 is requested by SLCD!

之所以有上面这么多的输出信息,是因为驱动文件中原来定义的调试宏被打开了,并且,你也可以在你自己觉得有疑问,需要打印的地方加入输出信息。 打开调试宏的办法: 例如在文件jz4740_slcd.c中,

//#undef DEBUG		//如果不想打印调试信息,那么就使用这一句
#define DEBUG		//如果想打印调试信息,使用这一句
#ifdef DEBUG
#define dprintk(x...)	printk(x)
#else
#define dprintk(x...)
#endif

原来#define DEBUG是被注释掉的,现在去掉注释,然后将#undef DEBUG注释掉。这样,下面文件中使用到的dprintk()函数就会使用printk()函数做内核的输出答应。

至于添加答应操作,那么就使用dprintk()函数就可以了,用法和printf()类似。